package com.treasure.mall.biz.service.core.auction.impl;

import com.treasure.mall.biz.base.*;
import com.treasure.mall.biz.bo.config.BasicsConfig;
import com.treasure.mall.biz.config.redis.RedisConstant;
import com.treasure.mall.biz.dto.BaseSaveDTO;
import com.treasure.mall.biz.dto.BaseSearchDTO;
import com.treasure.mall.biz.dto.auction.AuctionSaveDTO;
import com.treasure.mall.biz.dto.condition.AuctionSessionCondition;
import com.treasure.mall.biz.dto.order.OrderSaveDTO;
import com.treasure.mall.biz.entity.auction.AuctionBidLogPO;
import com.treasure.mall.biz.entity.auction.AuctionBidPO;
import com.treasure.mall.biz.entity.auction.AuctionSessionPO;
import com.treasure.mall.biz.entity.auction.AuctionSessionPO_;
import com.treasure.mall.biz.entity.embeddable.MerchantData_;
import com.treasure.mall.biz.entity.live.LiveSessionPO;
import com.treasure.mall.biz.entity.order.BizOrderPO;
import com.treasure.mall.biz.entity.order.DealOrderPO;
import com.treasure.mall.biz.entity.product.ProductPO;
import com.treasure.mall.biz.entity.product.ProductPO_;
import com.treasure.mall.biz.entity.user.MerchantPO_;
import com.treasure.mall.biz.entity.user.UserAddressPO;
import com.treasure.mall.biz.entity.user.UserPO;
import com.treasure.mall.biz.entity.user.UserPO_;
import com.treasure.mall.biz.remote.live.TencentIMService;
import com.treasure.mall.biz.remote.live.dto.AuctionStartMessageDTO;
import com.treasure.mall.biz.remote.live.dto.AuctionSuccessMessageDTO;
import com.treasure.mall.biz.remote.live.enums.MessageType;
import com.treasure.mall.biz.repository.auction.AuctionBidLogRepository;
import com.treasure.mall.biz.repository.auction.AuctionSessionRepository;
import com.treasure.mall.biz.repository.order.SubOrderRepository;
import com.treasure.mall.biz.service.common.ScheduledFutures;
import com.treasure.mall.biz.service.core.BaseConfigService;
import com.treasure.mall.biz.service.core.auction.AuctionBidService;
import com.treasure.mall.biz.service.core.auction.AuctionSessionService;
import com.treasure.mall.biz.service.core.live.LiveSessionService;
import com.treasure.mall.biz.service.core.order.BizOrderService;
import com.treasure.mall.biz.service.core.order.DealOrderService;
import com.treasure.mall.biz.service.core.pay.PayService;
import com.treasure.mall.biz.service.core.product.ProductService;
import com.treasure.mall.biz.service.core.user.UserAddressService;
import com.treasure.mall.zbq.common.Jsr310Utils;
import com.treasure.mall.zbq.common.MathUtils;
import com.treasure.mall.zbq.common.base.ApiResult;
import com.treasure.mall.zbq.common.base.ResultCodeEnum;
import com.treasure.mall.zbq.common.exception.ApiException;
import com.treasure.mall.zbq.common.ienum.auction.AuctionEnums;
import com.treasure.mall.zbq.common.ienum.base.CodeDescEnumHelper;
import com.treasure.mall.zbq.common.ienum.order.OrderEnums;
import com.treasure.mall.zbq.common.ienum.product.ProductEnums;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author allan
 * @version 1.0.0
 * @date 2020/7/16
 */
@Service
@Slf4j
public class AuctionSessionServiceImpl extends AbstractSearchableCrudService<AuctionSessionPO, Long> implements AuctionSessionService {
    private AuctionSessionRepository sessionRepository;
    @Autowired
    private ProductService productService;
    @Autowired
    private ThreadPoolTaskScheduler taskScheduler;
    @Autowired
    private AuctionBidService auctionBidService;
    @Autowired
    private BizOrderService bizOrderService;
    @Autowired
    private TencentIMService tencentIMService;
    @Autowired
    private RedisLocker redisLocker;
    @Autowired
    private AuctionBidLogRepository bidLogRepository;
    @Autowired
    private UserAddressService userAddressService;
    @Autowired
    private DealOrderService orderService;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Autowired
    private LiveSessionService liveSessionService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private Environment environment;
    @Autowired
    private SubOrderRepository subOrderRepository;
    @Resource
    private PayService payService;
    @Autowired
    private BaseConfigService baseConfigService;

    @Autowired
    protected AuctionSessionServiceImpl(AuctionSessionRepository repository) {
        super(repository);
        this.sessionRepository = repository;
    }

    @Override
    protected SpecificationAndSort<AuctionSessionPO> specificationAndSort(BaseSearchDTO searchDTO) {
        AuctionSessionCondition condition = (AuctionSessionCondition) searchDTO;
        UserDetails userDetails = AuthHelper.getPrincipal();
        if (AuthHelper.isMerchant(userDetails)) {
            condition.setMerchantId(userDetails.getId());
        }
        Specification<AuctionSessionPO> specification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (condition.getAuctionStatus() != -1) {
                AuctionEnums.AuctionStatus auctionStatus = CodeDescEnumHelper.getEnumTypeByCode(AuctionEnums.AuctionStatus.class, condition.getAuctionStatus());
                predicates.add(cb.equal(root.get(AuctionSessionPO_.auctionStatus), auctionStatus));
            }
            if (!StringUtils.isEmpty(condition.getProductName())) {
                predicates.add(cb.like(root.get(AuctionSessionPO_.productPO).get(ProductPO_.productName), likeAll(condition.getProductName())));
            }
            if (condition.getRoomId() != -1) {
                predicates.add(cb.equal(root.get(AuctionSessionPO_.productPO).get(ProductPO_.roomId), condition.getRoomId()));
            }
            if (!StringUtils.isEmpty(condition.getMerchantKey())) {
                predicates.add(cb.or(
                        cb.equal(root.get(AuctionSessionPO_.productPO).get(ProductPO_.merchantPO).get(MerchantPO_.user).get(UserPO_.nickName), likeAll(condition.getMerchantKey())),
                        cb.equal(root.get(AuctionSessionPO_.productPO).get(ProductPO_.merchantPO).get(MerchantPO_.user).get(UserPO_.mobile), likeAll(condition.getMerchantKey())),
                        cb.equal(root.get(AuctionSessionPO_.productPO).get(ProductPO_.merchantPO).get(MerchantPO_.data).get(MerchantData_.title), likeAll(condition.getMerchantKey()))
                ));
            }
            if (condition.getMerchantId() > 0) {
                predicates.add(cb.equal(root.get(AuctionSessionPO_.productPO).get(ProductPO_.merchantPO).get(MerchantPO_.merchantId), condition.getMerchantId()));
            }

            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };

        Sort sort = new Sort(Sort.Direction.ASC, "endTime");

        return new SpecificationAndSort<>(specification, sort);
    }

    @Override
    public AuctionSessionPO save(BaseSaveDTO saveDTO) {
        AuctionSaveDTO auctionSaveDTO = (AuctionSaveDTO) saveDTO;

        if (auctionSaveDTO.getSecurityDeposit() == null) {
            auctionSaveDTO.setSecurityDeposit(BigDecimal.ZERO);
        }
        AuctionSessionPO lastSession = sessionRepository.findTopByProductPO_ProductIdOrderByCreateTimeDesc(auctionSaveDTO.getProductId());
        if (lastSession != null && lastSession.getAuctionStatus() == AuctionEnums.AuctionStatus.IN_AUCTION) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "该商品当前有正在进行的拍卖"));
        }
        Long id = 0L;
        if (lastSession != null && lastSession.getAuctionStatus() == AuctionEnums.AuctionStatus.TO_START) {
            //修改的
            id = lastSession.getId();
        } else {
            /**
             * 添加拍卖规则
             * 1、判断是否有直播拍卖数据，如果没有拍卖规则数据，则可以添加拍卖规则，否则继续下一步判断。
             * 2、判断当前直播间的拍卖商品状态是否全部结束拍卖，全部结束拍卖，则可以添加拍卖规则，否则不能添加
             */

            //据房间ID，获取所有未结束的拍卖数据
            List<AuctionSessionPO> sessionPOList = sessionRepository.findByRoomIdNotAndAuctionStatus(auctionSaveDTO.getRoomId(), AuctionEnums.AuctionStatus.END);
            if (sessionPOList != null & sessionPOList.size() > 0) {
                throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "当前存在未开拍或拍卖中的商品"));
            }
        }


        //是否需要重设定时任务
        boolean needReset = false;
        AuctionSessionPO auctionSessionPO;
        LocalDateTime startTime = Jsr310Utils.DateTime.of(auctionSaveDTO.getStartTime());
        if (id > 0) {
            auctionSessionPO = findById(id);
            if (auctionSessionPO.getAuctionStatus() == AuctionEnums.AuctionStatus.IN_AUCTION) {
                throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "该商品正在进行拍卖，无法修改"));
            }
            if (!Jsr310Utils.DateTime.equalTo(startTime, auctionSessionPO.getStartTime())) {
                needReset = true;
            }
        } else {
            ProductPO productPO = productService.findById(auctionSaveDTO.getProductId());
            if (productPO.getSaleType() != ProductEnums.SaleType.AUCTION) {
                throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "当前商品不是拍卖商品"));
            }
            auctionSessionPO = new AuctionSessionPO();
            auctionSessionPO.setProductPO(productPO);
            auctionSessionPO.setAuctionStatus(AuctionEnums.AuctionStatus.TO_START);
        }
        auctionSessionPO.setRoomId(auctionSaveDTO.getRoomId());
        auctionSessionPO.setStartTime(startTime);
        LocalDateTime endTime;
        if (StringUtils.isEmpty(auctionSaveDTO.getPreEndTime())) {
            endTime = auctionSessionPO.getStartTime().plusMinutes(auctionSaveDTO.getDuration());
        } else {
            endTime = Jsr310Utils.DateTime.of(auctionSaveDTO.getPreEndTime());
            if (endTime.isBefore(LocalDateTime.now()) || endTime.isBefore(startTime)) {
                throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "结束时间必须大于当前时间和开始时间"));
            }
        }


        if (endTime.plusMinutes(1).compareTo(LocalDateTime.now()) <= 0) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "结束时间必须大于当前时间"));
        }


        auctionSessionPO.setEndTime(endTime);
        auctionSessionPO.setStartingPrice(auctionSaveDTO.getStartingPrice());
        auctionSessionPO.setUnitIncrease(auctionSaveDTO.getUnitIncrease());
        auctionSessionPO.setSecurityDeposit(auctionSaveDTO.getSecurityDeposit());
        auctionSessionPO.setDelay(auctionSaveDTO.getDelay() == 1);

        auctionSessionPO = save(auctionSessionPO);


        auctionSessionPO.getProductPO().setPrice(auctionSaveDTO.getStartingPrice());
        auctionSessionPO.getProductPO().setMktPrice(auctionSaveDTO.getStartingPrice());
        productService.save(auctionSessionPO.getProductPO());


        //加入schedule
        if (auctionSessionPO.getStartTime().isBefore(LocalDateTime.now())) {
            start(auctionSessionPO.getId());
        } else {
            if (needReset) {
                //重设schedule
                ScheduledFuture future = ScheduledFutures.startFuture.get(auctionSessionPO.getId());
                if (future != null) {
                    future.cancel(false);
                }
            }
            Long sessionId = auctionSessionPO.getId();
            ScheduledFuture future = taskScheduler.schedule(() -> start(sessionId), Jsr310Utils.DateTime.toDate(auctionSessionPO.getStartTime()));
            ScheduledFutures.startFuture.put(auctionSessionPO.getId(), future);
            //回写商品的状态
            auctionSessionPO.getProductPO().setAuctionStatus(AuctionEnums.AuctionStatus.TO_START);
            productService.save(auctionSessionPO.getProductPO());
        }

        return auctionSessionPO;
    }

    @Override
    public void start(Long sessionId) {
        log.info("开始拍卖===" + sessionId);
        AuctionSessionPO sessionPO = findById(sessionId);
        if (sessionPO.getAuctionStatus() != AuctionEnums.AuctionStatus.TO_START || sessionPO.getStartTime().isAfter(LocalDateTime.now())) {
            return;
        }
        sessionPO.setAuctionStatus(AuctionEnums.AuctionStatus.IN_AUCTION);

        save(sessionPO);

        sessionPO.getProductPO().setAuctionStatus(AuctionEnums.AuctionStatus.IN_AUCTION);
        productService.save(sessionPO.getProductPO());

        //加入结束的schedule
        ScheduledFuture future = taskScheduler.schedule(() -> end(sessionId), Jsr310Utils.DateTime.toDate(sessionPO.getEndTime()));
        ScheduledFutures.endFuture.put(sessionId, future);
        ScheduledFutures.startFuture.remove(sessionId);

        if (sessionPO.getProductPO().getRoomId() > 0) {
            //发送开拍通知
            tencentIMService.sendStartAuctionMessage(AuctionStartMessageDTO.builder().roomId(sessionPO.getProductPO().getRoomId()).build());
        }
    }


    @Override
    public AuctionSessionPO bid(UserPO userPO, Long sessionId) {
        AuctionSessionPO sessionPO;
        AuctionBidPO po;
        //得到当前的中标者
        while (true) {
            String lockKey = RedisConstant.LOCK_KEY_AUCTION_SESSION + sessionId;
            boolean locker = redisLocker.tryLock(lockKey, RedisConstant.LOCK_SERVICE_AUCTION, 10);
            if (locker) {
                try {
                    sessionPO = findById(sessionId);
                    if (sessionPO.getAuctionStatus() != AuctionEnums.AuctionStatus.IN_AUCTION) {
                        throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "当前拍卖未开始或已经结束"));
                    }
                    //判断是否有资格出价
                    BigDecimal securityDeposit = securityDeposit(userPO.getUserId(), sessionId);
                    if (MathUtils.greater(securityDeposit, BigDecimal.ZERO)) {
                        throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "没有交保证金，无法出价"));
                    }

                    AuctionBidPO winner = sessionPO.getWinner();
                    BigDecimal bidAmount;
                    if (winner == null) {
                        bidAmount = sessionPO.getStartingPrice().add(sessionPO.getUnitIncrease());
                    } else {
                        if (winner.getUserPO().getUserId().equals(userPO.getUserId())) {
                            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "当前您的出价已是最高"));
                        }

                        bidAmount = winner.getBidAmount().add(sessionPO.getUnitIncrease());
                        //原来的winner设为出局
                        winner.setSuccess(false);
                    }
                    po = auctionBidService.findByUserAndSession(userPO.getUserId(), sessionId);
                    if (po == null) {
                        po = new AuctionBidPO();
                        po.setUserPO(userPO);
                        po.setSessionPO(sessionPO);
                        //缴纳的保证金，可能为0，表示不需要交保证金
                        po.setSecurityDeposit(securityDeposit);
                    }
                    po.setBidAmount(bidAmount);
                    po.setSuccess(true);

                    auctionBidService.save(po);
                    auctionBidService.save(winner);

                    sessionPO.setWinner(po);
                    sessionPO = save(sessionPO);
                } finally {
                    redisLocker.release(lockKey, RedisConstant.LOCK_SERVICE_AUCTION);
                }
                break;
            }
        }
        //是否延长
        if (sessionPO.isDelay()) {
            long durationS = Jsr310Utils.DateTime.duration(LocalDateTime.now(), sessionPO.getEndTime(), TimeUnit.SECONDS);
            if (durationS < 10) {
                int delayCount = redisTemplate.getInt(RedisConstant.KEY_AUCTION_SESSION_DELAY_COUNT + sessionPO.getId());
                if (delayCount < 3) {
                    //小于10秒，延长10秒，但是最多3次
                    sessionPO.setEndTime(sessionPO.getEndTime().plusSeconds(10L));
                    sessionPO = save(sessionPO);
                    //重新设置时间
                    ScheduledFuture future = ScheduledFutures.startFuture.get(sessionId);
                    if (future != null) {
                        future.cancel(false);
                    }
                    //重新加入结束的schedule
                    future = taskScheduler.schedule(() -> end(sessionId), Jsr310Utils.DateTime.toDate(sessionPO.getEndTime()));
                    ScheduledFutures.endFuture.put(sessionId, future);

                    //增加该session延长的次数
                    redisTemplate.opsForValue().increment(RedisConstant.KEY_AUCTION_SESSION_DELAY_COUNT + sessionPO.getId(), 1);
                }
            }
        }

        //判断是否在结束前一分钟内，否则要延长5分钟
//        Duration duration = Duration.between(LocalDateTime.now(), sessionPO.getEndTime());
//        if (duration.toMinutes() < 1) {
//            sessionPO.setEndTime(sessionPO.getEndTime().plusMinutes(1L));
//            sessionPO = save(sessionPO);
//            //重新设置时间
//            ScheduledFuture future = AuctionScheduled.startFuture.get(sessionId);
//            if (future != null) {
//                future.cancel(false);
//            }
//            //重新加入结束的schedule
//            future = taskScheduler.schedule(() -> end(sessionId), Jsr310Utils.DateTime.toDate(sessionPO.getEndTime()));
//            AuctionScheduled.endFuture.put(sessionId, future);
//        }

        //出价成功通知
        AuctionSuccessMessageDTO messageDTO = AuctionSuccessMessageDTO.builder()
                .messageType(MessageType.auctionPlus)
                .roomId(sessionPO.getProductPO().getRoomId())
                .user(userPO)
                .auctionSessionId(sessionPO.getId())
                .money(po.getBidAmount())
                .build();

        tencentIMService.sendAuctionSuccess(messageDTO);

        //出价记录
        AuctionBidLogPO logPO = new AuctionBidLogPO(po);
        bidLogRepository.save(logPO);

        return sessionPO;
    }

    @Override
    public AuctionSessionPO currentSession(Long productId) {
        return sessionRepository.findTopByProductPO_ProductIdOrderByCreateTimeDesc(productId);
    }

    @Override
    public BigDecimal securityDeposit(Long userId, Long sessionId) {
        //先判断用户是否已经交了保证金，交过了肯定是可以的
        if (bizOrderService.enableAuction(userId, sessionId)) {
            return BigDecimal.ZERO;
        }
        BasicsConfig basicsConfig = baseConfigService.getBasicsConfig();
        //当前用户是否存在违规
        long payTimeoutCount = subOrderRepository.auctionCount(OrderEnums.PayStatus.TIME_OUT, userId, OrderEnums.OrderType.AUCTION);
        if (payTimeoutCount >= basicsConfig.getPayTimeoutCount()) {
            //需要缴纳保证金
            if (payTimeoutCount == basicsConfig.getPayTimeoutCount()) {
                //需要缴纳50元
                return basicsConfig.getSecurityDepositFirst();
//                if (environment.acceptsProfiles(Profiles.of(Constant.PROFILE_DEFAULT))) {
//                    return basicsConfig.getSecurityDepositFirst();
//                } else {
//                    return BigDecimal.valueOf(0.01);
//                }
            } else {
                //需要缴纳100元
                return basicsConfig.getSecurityDepositTwo();
//                if (environment.acceptsProfiles(Profiles.of(Constant.PROFILE_DEFAULT))) {
//                    return basicsConfig.getSecurityDepositTwo();
//                } else {
//                    return BigDecimal.valueOf(0.02);
//                }
            }
        }
        return BigDecimal.ZERO;
    }

    @Override
    public boolean enableBid(Long userId, Long sessionId) {
        AuctionSessionPO sessionPO = findById(sessionId);
        return bizOrderService.enableAuction(userId, sessionId) || MathUtils.equalTo(sessionPO.getSecurityDeposit(), BigDecimal.ZERO);
    }

    @Override
    public void initSchedule() {
        //获取开拍时间大于当前时间的数据
        List<AuctionSessionPO> toStart = sessionRepository.findByStartTimeGreaterThanEqual(LocalDateTime.now());
        for (AuctionSessionPO sessionPO : toStart) {
            long sessionId = sessionPO.getId();
            //加入结束的schedule
            ScheduledFuture future = taskScheduler.schedule(() -> start(sessionId), Jsr310Utils.DateTime.toDate(sessionPO.getStartTime()));
            ScheduledFutures.startFuture.put(sessionId, future);
            log.info("拍卖初始化加入开始定时任务==" + sessionId);
        }

        //获取结束时间大于或等于时间时间，状态为开拍中
        List<AuctionSessionPO> toEnd = sessionRepository.findByAuctionStatusAndEndTimeGreaterThanEqual(AuctionEnums.AuctionStatus.IN_AUCTION, LocalDateTime.now());
        for (AuctionSessionPO sessionPO : toEnd) {
            long sessionId = sessionPO.getId();
            //加入结束的schedule
            ScheduledFuture future = taskScheduler.schedule(() -> end(sessionId), Jsr310Utils.DateTime.toDate(sessionPO.getEndTime()));
            ScheduledFutures.endFuture.put(sessionId, future);
            log.info("拍卖初始化加入结束定时任务==" + sessionId);
        }

        //获取已经结束，但是状态还是开拍中的
        List<AuctionSessionPO> list = sessionRepository.findByAuctionStatusAndEndTimeLessThanEqual(AuctionEnums.AuctionStatus.IN_AUCTION, LocalDateTime.now());
        if (list != null) {
            for (AuctionSessionPO sessionPO : list) {
                end(sessionPO.getId());
                //加入结束的schedule
//                ScheduledFutures.endFuture.put(sessionPO.getId(), taskScheduler.schedule(() -> end(sessionPO.getId()), Jsr310Utils.DateTime.toDate(sessionPO.getEndTime())));
            }
        }

    }

    @Override
    public void refund(Long sessionId) {
        threadPoolTaskExecutor.execute(() -> {
            //未中拍的退还保证金
            List<AuctionBidPO> auctionBidPOS = auctionBidService.findLosers(sessionId);
            //退还保证金
            for (AuctionBidPO auctionBidPO : auctionBidPOS) {
                BizOrderPO bizOrderPO = bizOrderService.findByAuctionAndUser(auctionBidPO.getUserPO().getUserId(), sessionId);
                if (bizOrderPO != null) {
                    bizOrderService.refund(bizOrderPO);
                }
            }
        });
    }

    @Override
    public Page<AuctionBidLogPO> bidLogs(int pageIndex, int pageSize, Long sessionId) {
        return bidLogRepository.findBySessionIdOrderByCreateTimeDesc(sessionId, PageRequest.of(pageIndex - 1, pageSize));
    }

    @Override
    public void end(Long sessionId) {
        log.info("结束拍卖===" + sessionId);
        AuctionSessionPO sessionPO = findById(sessionId);
        if (sessionPO.getEndTime().isAfter(LocalDateTime.now()) || sessionPO.getAuctionStatus() != AuctionEnums.AuctionStatus.IN_AUCTION) {
            return;
        }
        sessionPO.setAuctionStatus(AuctionEnums.AuctionStatus.END);
        ScheduledFutures.endFuture.remove(sessionId);

        save(sessionPO);
        //回写商品的拍卖状态
        if (sessionPO.getWinner() != null) {
            sessionPO.getProductPO().setPrice(sessionPO.getWinner().getBidAmount());
            sessionPO.getProductPO().setPrice(sessionPO.getWinner().getBidAmount());
        }
        sessionPO.getProductPO().setAuctionStatus(AuctionEnums.AuctionStatus.END);
        productService.save(sessionPO.getProductPO());
        if (sessionPO.getWinner() != null) {
            //创建订单
            OrderSaveDTO orderSaveDTO = new OrderSaveDTO();
            orderSaveDTO.setUserId(sessionPO.getWinner().getUserPO().getUserId());
            UserAddressPO userAddressPO = userAddressService.findDefault(sessionPO.getWinner().getUserPO().getUserId());
            if (userAddressPO != null) {
                orderSaveDTO.setReceivingInfo(userAddressPO.getReceivingInfo());
            }
            orderSaveDTO.setProductInfo(new String[]{sessionPO.getProductPO().getProductId() + ":1"});
            orderSaveDTO.setOrderType(OrderEnums.OrderType.AUCTION);
            orderSaveDTO.setAuctionSessionId(sessionId);
            //得到当前的直播场次
            if (sessionPO.getProductPO().getRoomId() > 0) {
                LiveSessionPO currentSession = liveSessionService.getCurrentSession(sessionPO.getProductPO().getRoomId());
                if (currentSession != null) {
                    orderSaveDTO.setLiveSessionId(currentSession.getId());
                }
            }
            DealOrderPO dealOrderPO = orderService.create(orderSaveDTO);
            //绑定winner对应的订单
            sessionPO.getWinner().setDealOrderId(dealOrderPO.getOrderId());
            sessionPO.getWinner().setPayExpireTime(dealOrderPO.getPayExpireTime());
            auctionBidService.save(sessionPO.getWinner());

            if (sessionPO.getProductPO().getRoomId() > 0) {
                // 中标通知
                AuctionSuccessMessageDTO messageDTO = AuctionSuccessMessageDTO.builder()
                        .messageType(MessageType.auction)
                        .roomId(sessionPO.getProductPO().getRoomId())
                        .auctionSessionId(sessionPO.getId())
                        .user(sessionPO.getWinner().getUserPO())
                        .money(sessionPO.getWinner().getBidAmount())
                        .orderId(dealOrderPO.getOrderId())
                        .build();
                tencentIMService.sendAuctionSuccess(messageDTO);
            }


            //支付超时
            taskScheduler.schedule(() -> payService.payTimeout(dealOrderPO.getOrderId()), Jsr310Utils.DateTime.toDate(dealOrderPO.getPayExpireTime()));


            //退还保证金
            this.refund(sessionId);
        } else {
            if (sessionPO.getProductPO().getRoomId() > 0) {
                //发送流拍通知
                tencentIMService.sendStopAuctionMessage(AuctionStartMessageDTO.builder().roomId(sessionPO.getProductPO().getRoomId()).build());
            }
        }
    }
}
